/* eslint-disable indent */
const path = require('path');
const fs = require('fs');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const deepClone = require('lodash.clonedeep');
const postcssConfig = require('./postcssConfig');
const getBabelConfig = require('./getBabelConfig');
const getUserBuilderConfig = require('../utils/getUserBuilderConfig');


const SKIN_LOADER = require.resolve('ice-skin-loader');

const TYPESCRIPT_LOADER = require.resolve('ts-loader');
const BABEL_LOADER = require.resolve('babel-loader');
const CSS_LOADER = require.resolve('css-loader');
const LESS_LOADER = require.resolve('less-loader');
const STYL_LOADER = require.resolve('stylus-loader');
const POSTCSS_LOADER = require.resolve('postcss-loader');
const SASS_LOADER = require.resolve('sass-loader');
const CSS_HOT_LOADER = require.resolve('css-hot-loader');
const URL_LOADER = require.resolve('url-loader');

const EXCLUDE_REGX = /node_modules/;
const URL_LOADER_LIMIT = 8192;

const defaultAssetsLoaderOpts = {
  name: 'assets/[hash].[ext]',
  limit: URL_LOADER_LIMIT,
};

const cssLoaderOpts = {
  sourceMap: true,
};

const cssModuleLoaderOpts = {
  ...cssLoaderOpts,
  modules: true,
  localIdentName: '[folder]--[local]--[hash:base64:7]',
};

const userBuilderConfig = getUserBuilderConfig();

module.exports = (chainConfig, mode = 'development') => {
  const babelConfig = getBabelConfig();

  function setExtraCSSLoader(lang, loaders) {
    const moduleTestReg = new RegExp(`\\.module\\.${lang}$`);
    const cssTestReg = new RegExp(`\\.${lang}$`);

    for (let i = 0; i < 2; i++) {
      const isModule = !!i;
      const ruleKey = `${lang}${isModule ? '-module' : ''}`;

      let rule = chainConfig.module.rule(ruleKey);

      if (isModule === true) {
        rule = rule.test(moduleTestReg);
      } else {
        rule = rule.test(cssTestReg);
        rule.exclude.add(moduleTestReg);
      }

      if (mode !== 'production') {
        rule.use('css-hot-loader')
          .loader(CSS_HOT_LOADER);
      }

      rule.use('MiniCssExtractPlugin.loader')
        .loader(MiniCssExtractPlugin.loader);

      rule.use('css-loader')
        .loader(CSS_LOADER)
        .options(isModule ? cssModuleLoaderOpts : cssLoaderOpts);

      rule.use('postcss-loader')
        .loader(POSTCSS_LOADER)
        .options(Object.assign({ sourceMap: true }, postcssConfig));

      if (loaders && loaders.length > 0) {
        loaders.forEach((loader) => {
          const [loaderName, loaderPath, loaderOptions] = loader;

          rule.use(loaderName)
            .loader(loaderPath)
            .options(Object.assign({ sourceMap: true }, loaderOptions));
        });
      }
    }
  }

  function setAssetsLoader(type, test, loaderOpts) {
    const rule = chainConfig.module.rule(type).test(test);

    rule.use(type)
      .loader(URL_LOADER)
      // keep defaultAssetsLoaderOpts immutable
      .options(Object.assign({}, defaultAssetsLoaderOpts, loaderOpts));
  }

  // css loader

  /**
   * 换肤功能
   */
  const lessOptConfig = [['less-loader', LESS_LOADER, { sourceMap: true, javascriptEnabled: true, modifyVars: {} }]];
  const stylusOptConfig = [['stylus-loader', STYL_LOADER, {}]];


  /**
   * 自定义皮肤 目前只支持 stylus, less, sass因为不好装包暂时不支持
   * 通过读ice-skin-loader源码发现它本身不支持多个 themeFile 不支持多个皮肤
   */
  if (userBuilderConfig.themeSkins && userBuilderConfig.themeSkins.themeFile) {
    // 要支持多个皮肤包，主要考虑到 1.有皮肤包 2.项目自定义皮肤
    const themeFiles = [].concat(userBuilderConfig.themeSkins.themeFile);

    themeFiles.forEach(function (item) {

      const stylextname = path.extname(item);
      if (stylextname === '.styl') {
        stylusOptConfig.push(['ice-skin-loader', SKIN_LOADER, {
          themeFile: item,
          themeConfig: {}
        }]);
      } else if (stylextname === '.less') {
        Object.assign(lessOptConfig[0][2].modifyVars, readLessThemeFile(item));
      }

    });

  }

  setExtraCSSLoader('css');
  setExtraCSSLoader('scss', [['sass-loader', SASS_LOADER, {}]]);

  setExtraCSSLoader('less', lessOptConfig);
  setExtraCSSLoader('styl', stylusOptConfig);

  // assets loader
  setAssetsLoader('woff2', /\.woff2?$/, { mimetype: 'application/font-woff' });
  setAssetsLoader('ttf', /\.ttf$/, { mimetype: 'application/octet-stream' });
  setAssetsLoader('eot', /\.eot$/, { mimetype: 'application/vnd.ms-fontobject' });
  setAssetsLoader('svg', /\.svg$/, { mimetype: 'image/svg+xml' });
  setAssetsLoader('img', /\.(png|jpg|jpeg|gif)$/i);

  // jsx loader
  chainConfig.module.rule('jsx')
    .test(/\.jsx?$/)
    .exclude
    .add(EXCLUDE_REGX)
    .end()
    .use('babel-loader')
    .loader(BABEL_LOADER)
    .options(Object.assign({}, deepClone(babelConfig), { cacheDirectory: true }));

  // tsx loader
  chainConfig.module.rule('tsx')
    .test(/\.tsx?$/)
    .exclude
    .add(EXCLUDE_REGX)
    .end()
    .use('babel-loader')
    .loader(BABEL_LOADER)
    .options(Object.assign({}, deepClone(babelConfig), { cacheDirectory: true }))
    .end()
    .use('ts-loader')
    .loader(TYPESCRIPT_LOADER)
    .options({ transpileOnly: true });
};


/**
 * 读less皮肤配置文件
 * @param {string} less - less文件路径
 * @returns {object} 返回一个颜色键值对 对象出去
 */
function readLessThemeFile(less) {
  const result = {};
  if (!fs.existsSync(less)) {
    console.error(`File: ${less} Not Fount.`);
    process.exit(1);
  }
  const content = fs.readFileSync(less).toString('utf-8');

  (content.match(/@.+/mg) || [])
    .forEach(item => {
      const vals = item.split(/\s*:\s*/);
      result[vals[0]] = vals[1];
    })

  return result;
}
